#include <jni.h>
#include <string>
#include <android/log.h>
#include "cpp/verify_signature.h"
#include "cpp/key.h"
#include <iostream>

#define DEBUG true
#define LOG_TAG "jeff"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) // 定义LOGD类型

using namespace std;

jbyteArray getAESKey(JNIEnv *env) {
    int length = sizeof(AES_KEY) / sizeof(AES_KEY[0]);
    if (DEBUG) LOGD("AES_KEY length = %d", length);
    if (length <= 0)
        return NULL;

    jbyteArray key = env->NewByteArray(length);
    jbyte tmp[length];

    for (int i = 0; i < length; ++i) {
        jbyte b = AES_KEY[i] * i + i; // 自定义key规则，提高安全性
        tmp[i] = b;
    }
    env->SetByteArrayRegion(key, 0, length, tmp);

    return key;
}

jbyteArray getIvPara(JNIEnv *env) {
    int iv_length = strlen(IVPARA);
    jbyteArray iv = env->NewByteArray(iv_length);
    env->SetByteArrayRegion(iv, 0, iv_length, (jbyte *) IVPARA);
    return iv;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_jeff_cryptor_lib_AESNative_verify(JNIEnv *env, jobject, jobject context) {
    // Context 类
    jclass context_clazz = env->GetObjectClass(context);
    // getPackageManager 方法ID
    jmethodID methodID_getPackageManager = env->GetMethodID(context_clazz, "getPackageManager",
                                                            "()Landroid/content/pm/PackageManager;");

    // PackageManage 对象
    jobject pm = env->CallObjectMethod(context, methodID_getPackageManager);
    // PackageManage 类
    jclass pm_clazz = env->GetObjectClass(pm);

    // getPackageInfo 方法ID
    jmethodID methodID_pm = env->GetMethodID(pm_clazz, "getPackageInfo",
                                             "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

    // getPackageName 方法ID
    jmethodID methodID_pack = env->GetMethodID(context_clazz, "getPackageName",
                                               "()Ljava/lang/String;");

    // 应用包名
    jstring application_package = (jstring) env->CallObjectMethod(context, methodID_pack);

    const char *package_name = env->GetStringUTFChars(application_package, JNI_FALSE);

    if (DEBUG) LOGD("Application package name:%s", package_name);

    // PackageInfo 对象
    jobject packageInfo = env->CallObjectMethod(pm, methodID_pm, application_package, 64);
    // PackageInfo 类
    jclass packageinfo_clazz = env->GetObjectClass(packageInfo);

    jfieldID fieldID_signatures = env->GetFieldID(packageinfo_clazz, "signatures",
                                                  "[Landroid/content/pm/Signature;");

    jobjectArray signature_arr = (jobjectArray) env->GetObjectField(packageInfo,
                                                                    fieldID_signatures);

    //Signature数组中取出第一个元素
    jobject signature = env->GetObjectArrayElement(signature_arr, 0);

    //读signature的hashcode
    jclass signature_clazz = env->GetObjectClass(signature);
    jmethodID methodID_hashcode = env->GetMethodID(signature_clazz, "hashCode", "()I");
    jint hashCode = env->CallIntMethod(signature, methodID_hashcode);

    if (DEBUG) LOGD("Hash code value = %d", hashCode);

    // 包名校验
    if (strcmp(package_name, app_packageName) != 0) {
        return -1;
    }

    if (DEBUG) LOGD("1111111111111111");

    // 签名校验
    if (hashCode != app_signature_hash_code) {
        return -2;
    }

    if (DEBUG) LOGD("222222222222222");

    return 1; // check success
}

/*
 * Class:     com_jeff_cryptor_lib_AESNative
 * Method:    encrypt
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
extern "C"
JNIEXPORT jstring JNICALL Java_com_jeff_cryptor_lib_AESNative_encrypt
        (JNIEnv *env, jclass, jstring data) {

    if (DEBUG) LOGD("encrypt ...");

    jclass aes_clazz = env->FindClass("com/jeff/cryptor/lib/AES");
    if (aes_clazz == NULL)
        return NULL;

    jmethodID encryptID = env->GetStaticMethodID(aes_clazz, "encrypt",
                                                 "(Ljava/lang/String;[B[BLjava/lang/String;)Ljava/lang/String;");
    if (NULL == encryptID) {
        env->DeleteLocalRef(aes_clazz);
        return NULL;
    }

    // key
    jbyteArray key = getAESKey(env);
    if (NULL == key)
        return NULL;

    // iv
    jbyteArray iv = getIvPara(env);
    if (NULL == iv)
        return NULL;

    // algorithm
    jstring algo = env->NewStringUTF(ALGORITHM);


    jstring result = (jstring) env->CallStaticObjectMethod(aes_clazz, encryptID, data, key, iv,
                                                           algo);

    if (result == NULL) {
        if (DEBUG) LOGD("encrypt failed.");
    }

    return result;
}


/*
 * Class:     com_jeff_cryptor_lib_AESNative
 * Method:    decrypt
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
extern "C"
JNIEXPORT jstring JNICALL Java_com_jeff_cryptor_lib_AESNative_decrypt
        (JNIEnv *env, jclass, jstring data) {

    jclass aes_clazz = env->FindClass("com/jeff/cryptor/lib/AES");
    if (aes_clazz == NULL)
        return NULL;

    jmethodID encryptID = env->GetStaticMethodID(aes_clazz, "decrypt",
                                                 "(Ljava/lang/String;[B[BLjava/lang/String;)Ljava/lang/String;");
    if (NULL == encryptID) {
        env->DeleteLocalRef(aes_clazz);
        return NULL;
    }

    // key
    jbyteArray key = getAESKey(env);
    if (NULL == key)
        return NULL;

    // iv
    jbyteArray iv = getIvPara(env);
    if (NULL == iv)
        return NULL;

    // algorithm
    jstring algo = env->NewStringUTF(ALGORITHM);

    jstring result = (jstring) env->CallStaticObjectMethod(aes_clazz, encryptID, data, key, iv,
                                                           algo);

    if (result == NULL) {
        if (DEBUG) LOGD("decrypt failed.");
    }

    return result;
}